#include <iostream>
#include <functional>
#include <string>
#include <unordered_map>
#include <cstring>
#include <strings.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"

#define defaultIp "0.0.0.0"
#define defaultPort 8080

#define BUFF_SIZE 1024

// 因为参数不会被修改，可以使用const修饰和引用
using func_t = std::function<std::string(const std::string &)>;

enum
{
    SOCKET_ERROR = 1,
    BIND_ERROE,
    RECEIVE_ERROR
};

Log lg;

class Server
{
public:
    // 端口号写在前面，因为ip可能用默认的就行
    // Server(const std::string &ip = defaultIp, uint16_t port = defaultPort)
    Server(uint16_t port = defaultPort, const std::string &ip = defaultIp)
        : ip_(ip), port_(port), sockfd_(0), isRunning_(false)
    {
    }

    void Init()
    {
        sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);
        if (sockfd_ < 0)
        {
            lg(Fatal, "Server, socket error, errno: %d, %s", errno, strerror(errno));
            exit(SOCKET_ERROR);
        }
        lg(Info, "Server, socket success, sockfd: %d", sockfd_);

        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = inet_addr(ip_.c_str());

        socklen_t len = sizeof(local);

        if (bind(sockfd_, (sockaddr *)&local, len) < 0)
        {
            lg(Fatal, "server, bind error, errno: %d, %s", errno, strerror(errno));
            exit(BIND_ERROE);
        }
        lg(Info, "Server, bind success");
    }

    // bool CheckUser(const std::string &server_ip, const struct sockaddr_in &server, uint16_t server_port)
    bool CheckUser(const std::string &client_ip, const struct sockaddr_in &client, uint16_t client_port) // 参数名改为client比较好，因为本文件为server的
    {
        auto iter = online_user_.find(client_ip);
        if (iter == online_user_.end())
        {
            online_user_[client_ip] = client;
            // std::cout << "[" << client_ip << ":" << std::to_string(ntohs(client_port)) << "]coming" << std::endl;
            std::cout << "[" << client_ip << ":" << std::to_string(client_port) << "]coming" << std::endl;
            return false;
        }
        return true;
    }

    // void Broadcast(const std::string &info, const struct sockaddr_in &server)
    void Broadcast(const std::string &info, const std::string &client_ip, uint16_t client_port)
    {
        std::string message = "[";
        // message += inet_ntoa(server.sin_addr);
        message += client_ip;
        // std::cout<<message <<std::endl;
        message += ":";
        // std::cout<<message <<std::endl;
        //  message += std::to_string(ntohs(server.sin_port));      //要进行转换，否则可能出现乱码。需要转成字符串，否则也是乱码
        message += std::to_string(client_port);
        // std::cout<<message <<std::endl;
        message += "]say:";
        // std::cout<<message <<std::endl;
        message += info;
        // std::cout<<message <<std::endl;

        for (const auto &user : online_user_) // 不会被修改，可加const修饰
        {
            // 是发送给user.second，而不是给server，不要写错
            sendto(sockfd_, message.c_str(), message.size(), 0, (const sockaddr *)&(user.second), sizeof(user.second));
        }
    }

    // void Run(func_t func)
    void Run()
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        char buff[BUFF_SIZE];

        // 可以使用isRunning_作为下方循环的判断条件，这样就不需要用死循环了
        isRunning_ = true;

        // while (true)
        while (isRunning_)
        {
            // sockaddr_in对象和len的定义可以放于此处，如果下方的recvfrom修改了，sendto发出的对象也会相应的修改
            // 但如果只有一个client，那可以定义在循环外部，这样就不会造成重复定义

            // struct sockaddr_in client;
            // socklen_t len = sizeof(client);

            // 大小需为buff大小减一，因为最后一个位置要预备给\0
            ssize_t n = recvfrom(sockfd_, buff, BUFF_SIZE - 1, 0, (sockaddr *)&client, &len);
            if (n < 0)
            {
                lg(Error, "Server, receive (recvfrom) error, errno: %d, %s", errno, strerror(errno));

                // 如果小于0，接收错误，可以返回循环头部继续进行，而不是直接退出
                // exit(RECEIVE_ERROR);
                continue;
            }
            buff[n] = 0;
            // std::string ret = func(buff);
            // //std::cout << ret << std::endl;
            // sendto(sockfd_, ret.c_str(), ret.size(), 0, (sockaddr *)&client, len);

            // CheckUser(inet_ntoa(client.sin_addr), client, client.sin_port);
            // Broadcast(buff, client);

            // 因为下面两个函数只用到了client的几个成员，所以，无需将client传过去，即无需在对应函数内进行转换，因为两个函数可能转换的是同一个成员
            uint16_t client_port = ntohs(client.sin_port);
            std::string client_ip = inet_ntoa(client.sin_addr);
            CheckUser(client_ip, client, client_port);
            Broadcast(buff, client_ip, client_port);
        }
    }

    ~Server()
    {
        if (sockfd_ > 0)
            close(sockfd_);
    }

private:
    int sockfd_;
    std::string ip_;
    uint16_t port_;
    bool isRunning_;
    std::unordered_map<std::string, sockaddr_in> online_user_;
};